<template>
  <view class="lsj-file" ref="lsjFile" :style="[getStyles]">
    <!-- #ifdef APP-PLUS -->
    <view>
      <slot>
        <view class="defview" :style="[getStyles]">附件上传</view>
      </slot>
    </view>
    <!-- #endif -->

    <!-- #ifdef H5 -->
    <view class="hFile" :style="[getStyles]">
      <slot>
        <view class="defview" :style="[getStyles]">附件上传</view>
      </slot>
      <view v-show="!disabled" ref="hFile" class="hFile_input" :style="[getStyles]"></view>
    </view>
    <!-- #endif -->

    <!-- #ifdef MP-WEIXIN -->
    <view class="hFile" @click="wxChooseFile" :style="[getStyles]">
      <slot>
        <view class="defview" :style="[getStyles]">附件上传</view>
      </slot>
    </view>
    <!-- #endif -->
  </view>
</template>

<script>
  import CosAuth from '@/utils/cos-auth.min.js';

  export default {
    props: {
      value: [String, Number],
      uploadType: {
        type: String,
        default: '',
      },
      uploadKey: {
        type: String,
        default: ''
      },
      maxFileLength: {
        type: Number,
        default: 1,
      },
      uploadLength: {
        type: Number,
        default: 1,
      },
      width: {
        type: String,
        default: '100%',
      },
      height: {
        type: String,
        default: '80rpx',
      },
      size: {
        type: Number,
        default: 10,
      },
      // #ifndef APP-NVUE
      position: {
        type: String,
        default: 'static',
      },
      // #endif
      // #ifdef APP-NVUE
      position: {
        type: String,
        default: 'absolute',
      },
      // #endif

      childId: {
        type: String,
        default: 'lsjUpload',
      },
      top: [String, Number],
      left: [String, Number],
      bottom: [String, Number],
      right: [String, Number],
    },
    model: {
      prop: 'value',
      event: 'input',
    },
    data() {
      this.xmlRequest = '';
      this.wxParam = {};
      this.wv = {};
      return {
        fileDom: '',
        disabled: false,
        Bucket: 'gszuoye-1257050599',
        Region: 'ap-guangzhou',
        Signature: '',
        xCosSecurityToken: '',
        Key: '',
      };
    },
    computed: {
      getStyles() {
        let styles = {
          width: this.width,
          height: this.height,
        };
        if (this.position == 'absolute') {
          styles['top'] = this.top;
          styles['bottom'] = this.bottom;
          styles['left'] = this.left;
          styles['right'] = this.right;
          styles['position'] = 'fixed';
        }

        return styles;
      },
    },
    mounted() {},
    methods: {
      close() {
        // #ifdef APP-PLUS
        if (plus.webview.getWebviewById(this.childId)) {
          // #ifndef APP-NVUE
          this.wv.removeFromParent();
          // #endif
          plus.webview.close(this.childId, 'none', 0);
        }
        // #endif
        // #ifdef H5
        if (this.fileDom) {
          this.$refs.hFile.$el.removeChild(this.fileDom);
          this.fileDom = '';
        }
        // #endif
        // #ifdef MP-WEIXIN
        this.disabled = true;
        // #endif
      },
      create(param = {}) {
        this.uploadFile();
        console.log('加载秘钥')
        if (!param.url) {
          console.error('url为必传参数');
          return;
        }

        // #ifdef APP-PLUS
        if (!param.cuWebview) {
          console.error('cuWebview为必传参数');
          return;
        }
        this.close();

        // #ifndef APP-NVUE
        let view = uni.createSelectorQuery().in(this).select('.lsj-file');
        view
          .fields({
              size: true,
              rect: true,
            },
            ({
              height,
              width,
              top,
              left,
              right,
              bottom
            }) => {
              uni
                .createSelectorQuery()
                .selectViewport()
                .scrollOffset(({
                  scrollTop
                }) => {
                  let timeOut = 60;
                  const timer = setInterval(() => {
                    timeOut--;
                    if (timeOut > 0) {
                      if (this.xCosSecurityToken) {
                        this.createAppFile({
                            top: parseInt(top) + parseInt(scrollTop) + 'px',
                            left: parseInt(left) + 'px',
                            width: parseInt(width) + 'px',
                            height: parseInt(height) + 'px',
                          },
                          param
                        );
                        clearInterval(timer); //结束轮询
                      }
                    } else {
                      clearInterval(timer); //结束轮询
                    }
                  }, 200)
                })
                .exec();
            }
          )
          .exec();
        // #endif
        // #ifdef APP-NVUE
        const dom = weex.requireModule('dom');
        dom.getComponentRect(this.$refs.lsjFile, ({
          size: {
            height,
            width,
            top,
            left,
            right,
            bottom
          }
        }) => {
          this.createAppFile({
              top: parseInt(top) + 'px',
              left: parseInt(left) + 'px',
              width: parseInt(width) + 'px',
              height: parseInt(height) + 'px',
              right: parseInt(right) + 'px',
              bottom: parseInt(bottom) + 'px',
            },
            param
          );
        });
        // #endif

        // #endif

        // #ifdef MP-WEIXIN
        this.wxParam = param;
        this.disabled = false;
        // #endif

        // #ifdef H5
        this.createH5File(param);
        // #endif
      },
      toast(title = '', {
        duration = 2000,
        icon = 'none'
      } = {}) {
        uni.showToast({
          title,
          duration,
          icon,
        });
      },
      getRequest(url) {
        let theRequest = new Object();
        let index = url.indexOf('?');
        if (index != -1) {
          let str = url.substring(index + 1);
          let strs = str.split('&');
          for (let i = 0; i < strs.length; i++) {
            theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1]);
          }
        }
        return theRequest;
      },
      // 对更多字符编码的 url encode 格式,格式化地址使用
      camSafeUrlEncode(str) {
        return encodeURIComponent(str)
          .replace(/!/g, '%21')
          .replace(/'/g, '%27')
          .replace(/\(/g, '%28')
          .replace(/\)/g, '%29')
          .replace(/\*/g, '%2A');
      },
      wxChooseFile() {
        // #ifdef MP-WEIXIN
        if (this.disabled) {
          return;
        }
        wx.chooseMessageFile({
          count: 1,
          type: 'file',
          success: ({
            tempFiles
          }) => {
            /*   console.log('tempFiles', tempFiles[0]);
              console.log('uploadType', this.uploadType); */
            // 微信进行文件判断
            // console.log(this.uploadType,this.uploadType)
            //文件
            if (this.maxFileLength <= this.uploadLength) {
              uni.showToast({
                title: '超出上传数量',
                icon: 'none',
              });
              return
            }

            if (this.uploadType === 'any') {
              // 微信调用
              this.handleWXUpload(tempFiles[0]);
            } else {
              let uTypeArr = tempFiles[0].name.split('.');
              let uType = uTypeArr[uTypeArr.length - 1];
              switch (this.uploadType) {
                case 'word':
                  // 上传的不是指的格式返回false
                  if (/\.(doc|docx)$/.test('.' + uType)) {
                    // return true
                  } else {
                    uni.showToast({
                      title: '格式错误，请上传文档',
                      icon: 'none',
                    });
                    return;
                  }

                  break;
                case 'img':
                  // 上传的不是指的格式返回false
                  if (/\.(jpg|png|jpeg|gif|svg|webp)$/.test('.' + uType)) {
                    // return true
                  } else {
                    uni.showToast({
                      title: '格式错误，请上传图片',
                      icon: 'none',
                    });
                    return;
                  }
                  break;
                case 'video':
                  // 上传的不是指的格式返回false
                  if (/\.(mp4|fiv|wmv|avi|mov|mkv|m4v|vob|qsv)$/.test('.' + uType)) {
                    // return true
                  } else {
                    uni.showToast({
                      title: '格式错误，请上传视频',
                      icon: 'none',
                    });
                    return;
                  }
                  break;
              }
              // 微信调用
              this.handleWXUpload(tempFiles[0]);
            }

            // 如果有这个值什么类型相同
            // this.wxFile = tempFiles[0];
            // 将上传的文件传给父组件显示
            // this.$emit('transmitFile', this.wxFile);
          },
          fail: () => {
            this.$emit('callback', {
              success: false,
              status: -100,
              msg: '文件选择失败',
            });
          },
        });
        // #endif
      },
      // fhandleWXUpload() {
      //   this.handleWXUpload(this.wxFile);
      // },
      handleWXUpload(file) {
        this.wxParam['formData']['Signature'] = this.Signature;
        this.wxParam['formData']['x-cos-security-token'] = this.xCosSecurityToken;
        this.wxParam['formData']['key'] = '/yqyd/' + file.name.replace('.', new Date().getTime() + '.');
        let {
          debug = false, url, name = 'file', header = {}, formData = {}
        } = this.wxParam;
        if (!url) {
          console.error('url为必传参数');
          return;
        }
        if (file.size > 1024 * 1024 * Math.abs(this.size)) {
          this.toast(`附件大小请勿超过${this.size}M`);
          return;
        }

        formData['fileName'] = file.name;
        let opt = {
          url,
          name,
          header,
          formData,
          filePath: file.path,
        };
        //   debug &&
        //     console.log(`
        // 上传接口地址:${url}\n
        // 附件key:${name}\n
        // 附件名称:${file.name}\n
        // 附件大小:${file.size}\n
        // 请求头:${JSON.stringify(header)}\n
        // 参数:${JSON.stringify(formData)}
        // `);
        let filename = file.name;
        opt['fail'] = ({
          errMsg = ''
        }) => {
          this.disabled = false;
          console.error('--ERROR--：' + errMsg);
          this.$emit('callback', {
            success: false,
            status: 500,
            msg: '上传失败',
          });
        };
        opt['success'] = (res) => {
          this.disabled = false;
          if (res.statusCode == 200) {
            let geturl = url + this.camSafeUrlEncode(this.wxParam['formData']['key']).replace(/%2F/g, '/');
            debug && console.log('上传完成', geturl);
            this.$emit('callback', {
              success: true,
              fileName: filename,
              url: geturl,
              uploadType: this.uploadType,
              responseText: res.data,
              status: res.statusCode,
              uploadKey: this.uploadKey,
              msg: '上传成功',
            });
            return;
          }
          console.error('--ERROR--：status = ' + res.statusCode);
          this.$emit('callback', {
            success: false,
            status: res.statusCode,
            msg: '上传失败',
          });
        };
        this.disabled = true;
        this.xmlRequest = uni.uploadFile(opt);
        this.xmlRequest &&
          this.xmlRequest.onProgressUpdate(({
            progress = 0
          }) => {
            if (progress <= 100) {
              this.$emit('input', progress);
            }
          });
      },
      createH5File(param) {
        this.close();
        this.fileDom = document.createElement('input');
        this.fileDom.type = 'file';
        this.fileDom.value = '';
        this.fileDom.style.height = '80px';
        this.fileDom.style.width = '80px';
        this.fileDom.style.opacity = 0;
        this.$refs.hFile.$el.appendChild(this.fileDom);
        this.fileDom.onchange = (event) => {
          let file = event.target.files[0];
          if (file) {
            if (this.maxFileLength <= this.uploadLength) {
              uni.showToast({
                title: '超出上传数量',
                icon: 'none',
              });
              return
            }

            // 限制文件小于10M,可自行修改
            if (file.size > 1024 * 1024 * Math.abs(this.size)) {
              this.toast(`附件大小请勿超过${this.size}M`);
              return;
            }
            //  调用H5上传
            param['formData']['Signature'] = this.Signature;
            param['formData']['x-cos-security-token'] = this.xCosSecurityToken;
            param['formData']['key'] = '/yqyd/' + file.name.replace('.', new Date().getTime() + '.');
            // h5进行文件判断
            // console.log(this.uploadType,this.uploadType)
            //文件
            if (this.uploadType === 'any') {
              //调用提交
              this.uploadH5(file, param);
            } else {
              let uTypeArr = event.target.files[0].name.split('.');
              let uType = uTypeArr[uTypeArr.length - 1];
              switch (this.uploadType) {
                case 'word':
                  // 上传的不是指的格式返回false
                  if (/\.(doc|docx)$/.test('.' + uType)) {
                    // return true
                  } else {
                    uni.showToast({
                      title: '格式错误，请上传文档',
                      icon: 'none',
                    });
                    return;
                  }

                  break;
                case 'img':
                  // 上传的不是指的格式返回false
                  if (/\.(jpg|png|jpeg|gif|svg|webp)$/.test('.' + uType)) {
                    // return true
                  } else {
                    uni.showToast({
                      title: '格式错误，请上传图片',
                      icon: 'none',
                    });
                    return;
                  }
                  break;
                case 'video':
                  // 上传的不是指的格式返回false
                  if (/\.(mp4|fiv|wmv|avi|mov|mkv|m4v|vob|qsv)$/.test('.' + uType)) {
                    // return true
                  } else {
                    uni.showToast({
                      title: '格式错误，请上传视频',
                      icon: 'none',
                    });
                    return;
                  }
                  break;
              }
              //调用提交
              this.uploadH5(file, param);
            }

            // 如果有这个值什么类型相同
            // this.webFile = file;
            // this.webParam = param;
            // 将上传的文件传给父组件显示
            // this.$emit('transmitFile', this.webFile);
          }
        };
      },
      createAppFile(rect = {}, {
        cuWebview,
        ...param
      }) {
        param.size = this.size;
        param.uploadType = this.uploadType;
        param.maxFileLength = this.maxFileLength
        param.uploadLength = this.uploadLength
        param['formData']['Signature'] = this.Signature;
        param['formData']['x-cos-security-token'] = this.xCosSecurityToken;
        let wvPath = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
        let styles = {
          position: this.position,
          background: 'transparent',
        };

        // #ifdef APP-NVUE
        styles.position = 'absolute';
        // #endif

        styles = Object.assign(rect, styles);

        let wv = plus.webview.create(wvPath, this.childId, styles, param || {});
        wv.loadURL(wvPath);
        cuWebview.append(wv);
        this.wv = wv;
        wv.overrideUrlLoading({
            mode: 'reject',
          },
          (e) => {
            let {
              retype,
              percent = '',
              msg = '',
              fileName = '',
              responseText = '{}',
              success = false,
              geturl = ''
            } = this.getRequest(e.url);
            switch (retype) {
              case 'percent':
                percent = unescape(percent);
                this.$emit('input', Number(percent));
                break;
              case 'complete':
                msg = unescape(msg);
                fileName = unescape(fileName);
                responseText = unescape(responseText);
                geturl = unescape(geturl);
                this.$emit('callback', {
                  success,
                  fileName,
                  url: geturl,
                  uploadType: this.uploadType,
                  uploadKey: this.uploadKey,
                  responseText,
                  msg,
                });
                break;
              default:
                break;
            }
          }
        );
      },
      // fuploadH5() {
      //   this.uploadH5(this.webFile, this.webParam);
      // },
      uploadH5(file, {
        debug = false,
        url,
        name = 'file',
        method = 'POST',
        header = {},
        formData: data = {}
      }) {
        if (!url) {
          console.error('url为必传参数');
          return;
        }
        data['fileName'] = file.name;
        //   debug &&
        //     console.log(`
        // 上传接口地址:${url}\n
        // 附件key:${name}\n
        // 附件名称:${file.name}\n
        // 附件大小:${file.size}\n
        // 请求头:${JSON.stringify(header)}\n
        // 参数:${JSON.stringify(data)}
        // `);
        let filename = file.name;
        let formData = new FormData();
        for (let keys in data) {
          formData.append(keys, data[keys]);
        }
        formData.append(name, file);
        this.xmlRequest = new XMLHttpRequest();
        this.xmlRequest.open(method, url, true);
        for (let keys in header) {
          this.xmlRequest.setRequestHeader(keys, header[keys]);
        }

        this.xmlRequest.upload.addEventListener(
          'progress',
          (event) => {
            if (event.lengthComputable) {
              let percent = Math.ceil((event.loaded * 100) / event.total);
              if (percent <= 100) {
                this.$emit('input', percent);
              }
            }
          },
          false
        );

        this.xmlRequest.ontimeout = () => {
          this.disabled = false;
          this.fileDom.value = '';
          console.error('请求超时');
          this.$emit('callback', {
            success: false,
            status: 408,
            msg: '请求超时',
          });
        };

        this.xmlRequest.onreadystatechange = (ev) => {
          if (this.xmlRequest.readyState == 4) {
            this.disabled = false;
            this.fileDom.value = '';
            if (this.xmlRequest.status == 200) {
              let geturl = url + this.camSafeUrlEncode(data.key).replace(/%2F/g, '/');
              console.log('上传完成：', geturl);
              this.$emit('callback', {
                success: true,
                fileName: filename,
                url: geturl,
                uploadType: this.uploadType,
                uploadKey: this.uploadKey,
                responseText: this.xmlRequest.responseText,
                status: this.xmlRequest.status,
                msg: '上传成功',
              });
              return;
            } else if (this.xmlRequest.status == 0) {
              console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配，服务端已正确开启跨域，并且nginx未拦截阻止请求');
            }
            console.error('--ERROR--：status = ' + this.xmlRequest.status);
            this.$emit('callback', {
              success: false,
              status: this.xmlRequest.status,
              msg: '上传失败',
            });
          }
        };
        this.disabled = true;
        this.xmlRequest.send(formData);
      },
      // 获取秘钥
      uploadFile() {
        // 请求用到的参数
        let prefix = 'https://' + this.Bucket + '.cos.' + this.Region + '.myqcloud.com/';
        // 获取临时密钥
        let stsCache;
        let getCredentials = (callback) => {
          if (stsCache && Date.now() / 1000 + 30 < stsCache.expiredTime) {
            callback(data.credentials);
            return;
          }
          uni.request({
            method: 'GET',
            url: 'https://api.yqyd.gszuoye.com/api/oss/getOssTempSecret', // 服务端签名，参考上文说的获取临时密钥
            dataType: 'json',
            success: (result) => {
              let data = result.data;
              let credentials = data.credentials;
              if (credentials) {
                stsCache = data;
              } else {
                uni.showModal({
                  title: '临时密钥获取失败',
                  content: JSON.stringify(data),
                  showCancel: false,
                });
              }
              callback(stsCache && stsCache.credentials);
            },
            fail: (err) => {
              uni.showModal({
                title: '临时密钥获取失败',
                content: JSON.stringify(err),
                showCancel: false,
              });
            },
          });
        };

        // 计算签名
        let getAuthorization = (options, callback) => {
          getCredentials((credentials) => {
            callback({
              XCosSecurityToken: credentials.sessionToken,
              Authorization: CosAuth({
                SecretId: credentials.tmpSecretId,
                SecretKey: credentials.tmpSecretKey,
                Method: options.Method,
                Pathname: options.Pathname,
              }),
            });
          });
        };

        // 获取秘钥
        let uploadFile = (filePath) => {
          // let Key = filePath.substr(filePath.lastIndexOf('/') + 1); // 这里指定上传的文件名
          let signPathname = '/'; // PostObject 接口 Key 是放在 Body 传输，所以请求路径和签名路径是 /
          getAuthorization({
              Method: 'POST',
              Pathname: signPathname,
            },
            (AuthData) => {
              this.Signature = AuthData.Authorization;
              this.xCosSecurityToken = AuthData.XCosSecurityToken;
            }
          );
        };
        uploadFile();
      },
    },
  };
</script>

<style scoped lang="scss">
  .lsj-file {
    display: inline-block;
  }

  .defview {
    background-color: #007aff;
    color: #fff;
    border-radius: 10rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28rpx;
  }

  .hFile {
    position: relative;
  }

  .hFile_input {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
  }
</style>
